package edu.joe.game;

import javax.media.opengl.GL;
import javax.media.opengl.GL2;

import edu.joe.game.solvers.GravitySolver;
import edu.joe.game.solvers.NBodySolver;
import edu.joe.game.solvers.PhantomSolver;

public class ParticleSource {
	private ParticleRenderer renderer;
	private ParticleData[] particles;
	private int elapsedFrames;
	private double previousDt;

	ParticlePhysicsModel physics;
	ParticleGraphicsModel graphics;
	ParticleEmissionModel emitter;
	ParticleMotionSolver motion;

	/**
	 * Source and update logic. TODO make source have a x,y,z to parent
	 * particles to if needed
	 * 
	 * @param maxParticles
	 * @param burstCount
	 * @param burstRate
	 * @param particleModel
	 * @param graphicsModel
	 */
	public ParticleSource(ParticleEmissionModel emitterModel,
			ParticlePhysicsModel particleModel,
			ParticleGraphicsModel graphicsModel) {
		this.emitter = emitterModel;
		this.physics = particleModel;
		this.graphics = graphicsModel;
		particles = new ParticleData[this.emitter.maxParticles];
		for (int i = 0; i < this.emitter.maxParticles; i++)
			particles[i] = new ParticleData();
		renderer = new ParticleRenderer();
		this.motion = new PhantomSolver(particles, this.physics);
	}

	/**
	 * Create a bulk of particles, as many as you can that doesn't exceed the
	 * burst count per frame
	 */
	public void createParticles() {
		int burst = 0;
		// TODO stochastic sampling for particle creation
		for (int i = 0; i < particles.length && burst < this.emitter.burstCount; i++) {
			if (particles[i].life <= 0.0) {
				// Choose a random angle around the full circle
				double theta = Math.random() * Math.PI * 2;
				double mag = Math.random()
						* (physics.velocityMax - physics.velocityMin)
						+ physics.velocityMin;
				double radius = Math.sqrt(Math.random())
						* (physics.radiusMax - physics.radiusMin)
						+ physics.radiusMin;
				particles[i].life = (physics.lifeMax - physics.lifeMin)
						* Math.random() + physics.lifeMin;
				particles[i].y = Math.random() * (radius * 2) - radius;
				particles[i].z = Math.sqrt(radius * radius - particles[i].y
						* particles[i].y)
						* Math.cos(theta);
				particles[i].x = Math.sqrt(radius * radius - particles[i].y
						* particles[i].y)
						* Math.sin(theta);

				if (particles[i].y < 0 && physics.bounceHeight > particles[i].y
						&& physics.hitsFloor) {
					particles[i].y = physics.bounceHeight;
				}
				double norm = 1.0 / Math.sqrt(particles[i].x * particles[i].x
						+ particles[i].y * particles[i].y + particles[i].z
						* particles[i].z);
				particles[i].dx = particles[i].x * norm * mag + physics.dx;
				particles[i].dy = particles[i].y * norm * mag + physics.dy;
				particles[i].dz = particles[i].z * norm * mag + physics.dz;
				particles[i].fx = physics.fx;
				particles[i].fy = physics.fy;
				particles[i].fz = physics.fz;
				particles[i].ax = Math.random() * 360;
				particles[i].ay = Math.random() * 360;
				particles[i].az = Math.random() * 360;
				particles[i].ox = Math.random() * 90 - 45;
				particles[i].oy = Math.random() * 90 - 45;
				particles[i].oz = Math.random() * 90 - 45;
				double mc = Math.random();
				particles[i].mass = (mc*mc) * (physics.maxMass - physics.minMass) + physics.minMass;
				burst++;
			}
		}
	}

	public void update(double dt) {

		this.motion.update(dt);
		if (this.elapsedFrames > this.emitter.burstRate) {
			createParticles();
			this.elapsedFrames = 0;
		}
		elapsedFrames++;
	}

	public void render(GL2 gl, SceneCamera camera) {
		gl.glEnable(GL.GL_BLEND);
		gl.glDepthFunc(GL.GL_NEVER);
		gl.glDisable(GL.GL_DEPTH_TEST);
		if (graphics.glow) {
			gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE);
		} else {
			gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
		}
		for (int i = 0; i < particles.length; i++) {
			if (particles[i].life > 0) {
				double t = 1 - particles[i].life / physics.lifeMax;
				if (physics.exponential) {
					t = Math.sqrt(t);
				}
				particles[i].a = (graphics.aE - graphics.aS) * t + graphics.aS;
				particles[i].r = ((graphics.rE - graphics.rS) * t + graphics.rS)
						* particles[i].a;
				particles[i].g = ((graphics.gE - graphics.gS) * t + graphics.gS)
						* particles[i].a;
				particles[i].b = ((graphics.bE - graphics.bS) * t + graphics.bS)
						* particles[i].a;
				particles[i].w = (graphics.widthE - graphics.widthS) * t
						+ graphics.widthS;
				particles[i].h = (graphics.heightE - graphics.heightS) * t
						+ graphics.heightS;
				renderer.render(gl, camera, particles[i], graphics);
			}
		}
		gl.glDisable(GL.GL_BLEND);
	}
}
